package org.sean.framework.es;

import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.DocWriteRequest;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateRequest;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.GetAliasesResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CloseIndexRequest;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.DeleteAliasRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.PutIndexTemplateRequest;
import org.elasticsearch.cluster.metadata.AliasMetadata;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.sean.framework.bean.PageData;
import org.sean.framework.bean.PageQuery;
import org.sean.framework.bean.Sort;
import org.sean.framework.code.StatusInfo;
import org.sean.framework.exception.StatusException;
import org.sean.framework.util.GSONUtil;
import org.sean.framework.util.NumberUtil;
import org.sean.framework.util.ObjectUtil;
import org.sean.framework.util.StringUtil;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * ES 通用操作
 * https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-create-index.html
 */
@Slf4j
public final class CommonOperation {
    protected RestHighLevelClient client;

    protected RequestOptions options = RequestOptions.DEFAULT;

    public CommonOperation(RestHighLevelClient client) {
        this.client = client;
    }

    /**
     * 创建索引
     * <pre>
     * {@code
     * Map<String, Object> properties = new HashMap<>();
     * // 用户ID
     * Map<String, Object> uid = new HashMap<>();
     * uid.put("type", "integer");
     *
     * properties.put("uid", uid);
     *
     * createIndex("testIndex",properties);
     * }
     * </pre>
     *
     * @param index      index
     * @param properties index properties
     * @return 结果
     */
    public boolean createIndex(String index, Map<String, Object> properties) {
        try {
            return createIndex(index, null, properties);
        } catch (ElasticsearchStatusException e) {
            log.warn(e.getMessage(), e);
        } catch (Exception e) {
            throw new ElasticsearchException("create index error");
        }
        return false;
    }

    /**
     * 创建索引
     * <pre>
     * {@code
     * Map<String, Object> properties = new HashMap<>();
     * // 用户ID
     * Map<String, Object> uid = new HashMap<>();
     * uid.put("type", "integer");
     *
     * properties.put("uid", uid);
     *
     * createIndex("testIndex",properties);
     * }
     * </pre>
     *
     * @param index      index
     * @param alias      alias
     * @param properties index properties
     * @return 结果
     * @throws IOException Exception
     */
    public boolean createIndex(String index, String alias, Map<String, Object> properties) throws IOException {
        if (!checkIndexExist(index)) {
            CreateIndexRequest request = new CreateIndexRequest(index);
            if (ObjectUtil.isNotEmpty(properties)) {
                Map<String, Object> mappings = new HashMap<>();
                mappings.put("properties", properties);
                request.mapping(mappings);
            }

            client.indices().create(request, options);

            if (StringUtil.isNotEmpty(alias)) {
                IndicesAliasesRequest aliasesRequest = new IndicesAliasesRequest();
                IndicesAliasesRequest.AliasActions action =
                        new IndicesAliasesRequest.AliasActions(IndicesAliasesRequest.AliasActions.Type.ADD);
                action.alias(alias);
                action.index(index);
                aliasesRequest.addAliasAction(action);
                client.indices().updateAliases(aliasesRequest, options);
            }
            return true;
        }
        return false;
    }

    /**
     * 删除索引
     *
     * @param index index
     * @return 结果
     */
    public boolean deleteIndex(String index) {
        try {
            DeleteIndexRequest request = new DeleteIndexRequest(index);
            AcknowledgedResponse response = client.indices().delete(request, options);
            return response.isAcknowledged();
        } catch (ElasticsearchStatusException e) {
            return e.status().equals(RestStatus.NOT_FOUND);
        } catch (Exception e) {
            log.warn(e.getMessage(), e);
        }
        return false;
    }


    /**
     * 检查索引是否存在
     *
     * @param index index
     * @return 结果
     */
    public boolean checkIndexExist(String index) {
        try {
            return client.indices().exists(new GetIndexRequest(index), options);
        } catch (IOException e) {
            log.warn(e.getMessage(), e);
        }
        return false;
    }

    /**
     * 检查索引别名是否存在
     *
     * @param index index
     * @return 结果
     */
    public boolean checkIndexAliasExist(String index) {
        try {
            return client.indices().existsAlias(new GetAliasesRequest().indices(index), options);
        } catch (IOException e) {
            log.warn(e.getMessage(), e);
        }
        return false;
    }


    /**
     * 生成Request
     *
     * @param index index
     * @param id    id
     * @param data  数据
     * @return 结果
     */
    public IndexRequest indexRequest(String index, Serializable id, Object data) {
        return new IndexRequest(index).id(id.toString())
                .source(GSONUtil.obj2Json(data), XContentType.JSON);
    }

    /**
     * 新增
     *
     * @param obj   Object
     * @param index index
     * @param id    id
     * @return 结果
     */
    public boolean index(String index, String id, Object obj) {
        return index(indexRequest(index, id, obj));
    }

    /**
     * 新增
     *
     * @param request request
     * @return 结果
     */
    public boolean index(IndexRequest request) {
        IndexResponse response;
        try {
            response = client.index(request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL), options);
        } catch (ElasticsearchStatusException e) {
            throw new StatusException(StatusInfo.dataAccessFailed().setAnyMessage(e.status().name()));
        } catch (IOException e) {
            log.warn(e.getMessage(), e);
            return false;
        }
        return response.getResult() == DocWriteResponse.Result.CREATED;
    }

    /**
     * 生成Request
     *
     * @param index index
     * @param id    id
     * @param data  数据
     * @return 结果
     */
    public UpdateRequest updateRequest(String index, Serializable id, Object data) {
        if (id == null) {
            throw new StatusException(StatusInfo.paramsInvalidError());
        }
        UpdateRequest request = new UpdateRequest(index, id.toString());
        request.doc(GSONUtil.obj2Map(data));
        return request;
    }

    /**
     * 更新
     *
     * @param index index
     * @param id    id
     * @param data  数据
     * @return 结果
     */
    public boolean update(String index, Serializable id, Object data) {
        return update(updateRequest(index, id, data));
    }

    /**
     * 更新
     *
     * @param request request
     * @return 结果
     */
    public boolean update(UpdateRequest request) {
        UpdateResponse response;
        try {
            response = client.update(request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL), options);

            return response.getResult() == DocWriteResponse.Result.CREATED
                    || response.getResult() == DocWriteResponse.Result.UPDATED
                    || response.getResult() == DocWriteResponse.Result.NOOP;
        } catch (ElasticsearchStatusException e) {
            throw new StatusException(StatusInfo.dataAccessFailed().setAnyMessage(e.status().name()));
        } catch (IOException e) {
            log.warn(e.getMessage(), e);
            return false;
        }
    }

    /**
     * 生成Request
     *
     * @param index index
     * @param id    id
     * @return 结果
     */
    public DeleteRequest deleteRequest(String index, Serializable id) {
        if (id == null) {
            throw new StatusException(StatusInfo.paramsInvalidError());
        }
        return new DeleteRequest(index, id.toString());
    }

    /**
     * 删除
     *
     * @param index index
     * @param id    id
     * @return 结果
     */
    public boolean delete(String index, Serializable id) {
        if (id == null) {
            throw new StatusException(StatusInfo.paramsInvalidError());
        }
        return delete(deleteRequest(index, id));
    }

    /**
     * 删除
     *
     * @param request request
     * @return 结果
     */
    public boolean delete(DeleteRequest request) {
        try {
            DeleteResponse response = client.delete(request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL), options);
            return response.getResult() == DocWriteResponse.Result.DELETED
                    || response.getResult() == DocWriteResponse.Result.NOT_FOUND;
        } catch (ElasticsearchStatusException e) {
            if (RestStatus.NOT_FOUND.equals(e.status())) {
                return true;
            }
            throw new StatusException(StatusInfo.dataAccessFailed().setAnyMessage(e.status().name()));
        } catch (Exception e) {
            log.warn(e.getMessage(), e);
            return false;
        }
    }

    /**
     * 查询
     *
     * @param index index
     * @param id    id
     * @return 查询结果
     */
    public GetResponse get(String index, Serializable id) {
        if (id == null) {
            throw new StatusException(StatusInfo.paramsInvalidError());
        }
        return get(new GetRequest(index, id.toString()));
    }

    /**
     * 查询
     *
     * @param request 请求
     * @return 查询结果
     */
    public GetResponse get(GetRequest request) {
        try {
            return client.get(request, options);
        } catch (Exception e) {
            log.warn(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 批量操作
     * <p>
     * https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-document-bulk.html
     * </p>
     *
     * @param requests 请求
     * @return 处理结果
     */
    public BulkResult bulk(List<DocWriteRequest<?>> requests) {
        if (ObjectUtil.isEmpty(requests)) {
            throw new StatusException(StatusInfo.paramsInvalidError());
        }
        BulkRequest request = new BulkRequest();
        requests.forEach(request::add);
        return bulk(request);
    }

    /**
     * 批量处理
     *
     * @param request 查询请求
     * @return 查询结果
     */
    public BulkResult bulk(BulkRequest request) {
        request.timeout(TimeValue.timeValueMinutes(2));
        request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);

        BulkResult result = new BulkResult();

        BulkResponse bulkResponse;
        try {
            bulkResponse = client.bulk(request, RequestOptions.DEFAULT);
        } catch (ElasticsearchStatusException e) {
            throw new StatusException(StatusInfo.dataAccessFailed().setAnyMessage(e.status().name()));
        } catch (Exception e) {
            log.warn(e.getMessage(), e);
            return result;
        }

        for (BulkItemResponse bulkItemResponse : bulkResponse) {
            if (bulkItemResponse.isFailed()) {
                result.getFailures().add(bulkItemResponse.getFailure());
            }
            result.getResponses().add(bulkItemResponse.getResponse());
        }
        if (ObjectUtil.isNotEmpty(result.getFailures())) {
            log.error("Bulk Operation Error -> count : {}  -> {}",
                    result.getFailures().size(), result.getFailures().get(0).toString());
        }
        return result;
    }

    /**
     * 查询
     *
     * @param request 请求
     * @return 查询结果
     */
    public SearchResponse search(SearchRequest request) {
        try {
            return client.search(request, options);
        } catch (Exception e) {
            log.warn(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 查询所有符合条件的实体ID
     *
     * @param index     index name
     * @param example   条件
     * @param needScore 是否需要算分
     * @return 列表数据
     */
    public List<Integer> search4IntegerId(String index, QueryExample example, boolean needScore) {
        PageData<String> data = search4Id(index, example, needScore, false, null, null, null);
        if (data.getCount() <= 0) {
            return Collections.emptyList();
        }
        return data.getList().stream().map(NumberUtil::getInteger).collect(Collectors.toList());
    }

    /**
     * 查询所有符合条件的实体ID
     *
     * @param index     index name
     * @param example   条件
     * @param needScore 是否需要算分
     * @return 列表数据
     */
    public List<Long> search4LongId(String index, QueryExample example, boolean needScore) {
        PageData<String> data = search4Id(index, example, needScore, false, null, null, null);
        if (data.getCount() <= 0) {
            return Collections.emptyList();
        }
        return data.getList().stream().map(NumberUtil::getLong).collect(Collectors.toList());
    }

    /**
     * 查询所有符合条件的实体ID
     *
     * @param index     index name
     * @param example   条件
     * @param needScore 是否需要算分
     * @return 列表数据
     */
    public List<String> search4StringId(String index, QueryExample example, boolean needScore) {
        PageData<String> data = search4Id(index, example, needScore, false, null, null, null);
        return data.getList();
    }

    /**
     * 查询实体ID
     *
     * @param index          index name
     * @param example        条件
     * @param needScore      是否需要算分
     * @param fetchAll       查所有记录
     * @param page           分页
     * @param incrementParam 增量分页
     * @param sortParam      排序
     * @return 分页数据
     */
    public PageData<String> search4Id(String index, QueryExample example, boolean needScore,
                                      boolean fetchAll, PageQuery page,
                                      EntityIncrementParam<? extends Serializable> incrementParam,
                                      List<EntitySortParam> sortParam) {
        return search(index, example, needScore, SearchHit::getId, fetchAll, page, incrementParam, sortParam);
    }

    /**
     * 查询实体
     *
     * @param <T>            T
     * @param index          index name
     * @param example        条件
     * @param needScore      是否需要算分
     * @param page           分页
     * @param incrementParam 增量分页
     * @param sortParam      排序
     * @param type           实体类型
     * @return 分页数据
     */
    public <T extends Entity> PageData<T> search(String index, QueryExample example, boolean needScore,
                                                 PageQuery page,
                                                 EntityIncrementParam<? extends Serializable> incrementParam,
                                                 EntitySortParam sortParam, Type type) {
        List<EntitySortParam> sortParams = new ArrayList<>();
        if (sortParam != null) {
            sortParams.add(sortParam);
        }
        return search(index, example, needScore, false, page, incrementParam, sortParams, type);
    }

    /**
     * 查询实体
     *
     * @param <T>            T
     * @param index          index name
     * @param example        条件
     * @param needScore      是否需要算分
     * @param page           分页
     * @param incrementParam 增量分页
     * @param sortParam      排序
     * @param type           实体类型
     * @return 分页数据
     */
    public <T extends Entity> PageData<T> search(String index, QueryExample example, boolean needScore,
                                                 PageQuery page,
                                                 EntityIncrementParam<? extends Serializable> incrementParam,
                                                 List<EntitySortParam> sortParam, Type type) {
        return search(index, example, needScore, false, page, incrementParam, sortParam, type);
    }

    /**
     * 查询实体
     *
     * @param <T>            T
     * @param index          index name
     * @param example        条件
     * @param needScore      是否需要算分
     * @param fetchAll       是否获取全量数据
     * @param page           分页
     * @param incrementParam 增量分页
     * @param sortParam      排序
     * @param type           实体类型
     * @return 分页数据
     */
    public <T extends Entity> PageData<T> search(String index, QueryExample example, boolean needScore,
                                                 boolean fetchAll, PageQuery page,
                                                 EntityIncrementParam<? extends Serializable> incrementParam,
                                                 List<EntitySortParam> sortParam, Type type) {
        return search(index, example, needScore, hit -> {
            T entity = GSONUtil.map2Obj(hit.getSourceAsMap(), type);
            if (entity != null) {
                entity.setScore(hit.getScore());
            }
            return entity;
        }, fetchAll, page, incrementParam, sortParam);
    }

    public Aggregations searchAggregation(String index, QueryExample example) {
        try {
            SearchSourceBuilder searchSourceBuilder = createBuilder(example, false);
            searchSourceBuilder.fetchSource(false);
            SearchRequest searchRequest = new SearchRequest(index);
            searchRequest.source(searchSourceBuilder);
            if (log.isDebugEnabled()) {
                log.debug(searchRequest.toString());
            }
            // 查询数据
            SearchResponse searchResponse = client.search(searchRequest, options);
            return searchResponse.getAggregations();
        } catch (Exception e) {
            log.warn(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 查询实体
     *
     * @param <T>            T
     * @param index          index name
     * @param example        条件
     * @param needScore      是否需要算分
     * @param fun            转换对象
     * @param fetchAll       是否获取全量数据
     * @param page           分页信息
     * @param incrementParam 增量分页
     * @param sortParams     排序
     * @return 分页数据
     */
    public <T> PageData<T> search(String index, QueryExample example, boolean needScore, Function<SearchHit, T> fun,
                                  boolean fetchAll, PageQuery page,
                                  EntityIncrementParam<? extends Serializable> incrementParam,
                                  List<EntitySortParam> sortParams) {
        PageData<T> result = new PageData<>();
        if (example == null) {
            return result;
        }
        String scrollId = null;
        Set<String> scrollIds = new HashSet<>();
        try {
            SearchSourceBuilder searchSourceBuilder = createBuilder(example, needScore);
            if (example.isOnlyId()) {
                searchSourceBuilder.fetchSource(false);
            }
            // 没有分页信息,查询最大数据量
            if (page == null && incrementParam == null) {
                searchSourceBuilder.size(10000);
            } else if (fetchAll) {
                // 显示传参获取全部,使用scroll
                searchSourceBuilder.size(300);
            } else {
                // 有分页信息,按分页查询
                if (page != null) {
                    if (page.getOffset() + page.getSize() > 10000) {
                        throw new StatusException(StatusInfo.dataAccessFailed().setAnyMessage("此业务不能使用ES,请联系管理员"));
                    }
                    searchSourceBuilder.from(page.getOffset());
                    searchSourceBuilder.size(page.getSize());
                    result.setPage(page.getPage());
                    result.setSize(page.getSize());
                } else {
                    searchSourceBuilder.size(incrementParam.getSize());
                    if (incrementParam.getAfterValue() != null) {
                        searchSourceBuilder.searchAfter(new Serializable[]{incrementParam.getAfterValue()});
                    }
                }
            }
            if (ObjectUtil.isNotEmpty(sortParams)) {
                sortParams.forEach(sortParam -> {
                    if (sortParam != null && StringUtil.isNotEmpty(sortParam.getField())) {
                        searchSourceBuilder.sort(sortParam.getField(),
                                Sort.ASC.equals(sortParam.getSort()) ? SortOrder.ASC : SortOrder.DESC);
                    }
                });
            }
            SearchRequest searchRequest = new SearchRequest(index);
            searchRequest.source(searchSourceBuilder);
            List<T> list = new ArrayList<>();
            if (log.isDebugEnabled()) {
                log.debug(searchRequest.toString());
            }
            if (fetchAll) {
                searchRequest.scroll(TimeValue.timeValueSeconds(3));
                // 查询数据
                SearchResponse searchResponse = client.search(searchRequest, options);
                scrollId = searchResponse.getScrollId();
                scrollIds.add(scrollId);
                SearchHits searchHits = searchResponse.getHits();
                result.setCount(searchHits.getTotalHits().value);

                while (searchHits != null && searchHits.getHits() != null && searchHits.getHits().length > 0) {
                    // 处理数据
                    searchHits.forEach(hit -> list.add(fun.apply(hit)));
                    // 查询下一批数据
                    if (searchHits.getHits().length > 0) {
                        SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
                        scrollRequest.scroll(TimeValue.timeValueSeconds(10));
                        searchResponse = client.scroll(scrollRequest, options);
                        scrollId = searchResponse.getScrollId();

                        searchHits = searchResponse.getHits();
                    } else {
                        break;
                    }
                    if (scrollIds.size() % 10 == 0) {
                        clearScrollRequest(scrollIds);
                        scrollIds.clear();
                    }
                    scrollIds.add(scrollId);
                }
            } else {
                // 查询数据
                SearchResponse searchResponse = client.search(searchRequest, options);
                SearchHits searchHits = searchResponse.getHits();
                searchHits.forEach(hit -> list.add(fun.apply(hit)));
                result.setCount(searchHits.getTotalHits().value);
            }
            result.setList(list);
            return result;
        } catch (Exception e) {
            log.warn(e.getMessage(), e);
        } finally {
            clearScrollRequest(scrollIds);
        }
        return result;
    }

    /**
     * 创建索引模板
     *
     * @param request Request
     */
    public void createIndexTemplate(PutIndexTemplateRequest request) {
        try {
            AcknowledgedResponse response = client.indices().putTemplate(request, RequestOptions.DEFAULT);
            log.info("response :{}", GSONUtil.obj2Json(response));
        } catch (IOException e) {
            log.error("createIndexTemplate error!,request:{}", GSONUtil.obj2Json(request), e);
        }
    }

    public void deleteIndexTemplate(String templateName) {
        DeleteIndexTemplateRequest deleteRequest = new DeleteIndexTemplateRequest(templateName);
        try {
            AcknowledgedResponse response = client.indices()
                    .deleteTemplate(deleteRequest, RequestOptions.DEFAULT);
            log.info("deleteIndexTemplate templateName:{},response:{}", templateName, GSONUtil.obj2Json(response));
        } catch (IOException e) {
            log.error("deleteIndexTemplate error templateName:{},e", templateName, e);
        }
    }

    /**
     * 解除索引和别名之间的关联
     *
     * @param indexName 索引名
     * @param aliasName 别名
     * @throws StatusException StatusException
     */

    public void removeAlias(String indexName, String aliasName) {

        try {
            GetAliasesRequest getAliasesRequest = new GetAliasesRequest();
            getAliasesRequest.indices(indexName);
            GetAliasesResponse getAliasesResponse = client.indices().getAlias(getAliasesRequest, RequestOptions.DEFAULT);
            Map<String, Set<AliasMetadata>> aliases = getAliasesResponse.getAliases();

            if (null == aliases) {
                return;
            }

            Set<AliasMetadata> aliasMetadata = aliases.get(indexName);

            if (ObjectUtil.isNotEmpty(aliasMetadata)) {
                Set<String> existAliasNameSet = aliasMetadata.stream().map(AliasMetadata::getAlias)
                        .collect(Collectors.toSet());
                if (existAliasNameSet.contains(aliasName)) {
                    DeleteAliasRequest request = new DeleteAliasRequest(indexName, aliasName);
                    org.elasticsearch.client.core.AcknowledgedResponse deleteAliasResponse = client.indices().deleteAlias(request, RequestOptions.DEFAULT);
                    log.info("response :{}", GSONUtil.obj2Json(deleteAliasResponse));
                }
            }
        } catch (Exception e) {
            throw new StatusException(StatusInfo.dataAccessFailed(), e);
        }
    }

    /**
     * 关闭索引
     *
     * @param indexName 索引名
     * @throws Exception Exception
     */
    public void closeIndex(String indexName) throws Exception {
        CloseIndexRequest request = new CloseIndexRequest(indexName);
        AcknowledgedResponse closeIndexResponse = client.indices().close(request, RequestOptions.DEFAULT);
        log.info("closeIndex response:{}", GSONUtil.obj2Json(closeIndexResponse));
    }

    /**
     * 为索引增加别名
     *
     * @param indexName 索引名
     * @param aliasName 别名
     * @throws Exception Exception
     */
    public void addAlias(String indexName, String aliasName) throws Exception {
        IndicesAliasesRequest request = new IndicesAliasesRequest();
        IndicesAliasesRequest.AliasActions addIndexAction =
                new IndicesAliasesRequest.AliasActions(IndicesAliasesRequest.AliasActions.Type.ADD)
                        .index(indexName)
                        .alias(aliasName);
        request.addAliasAction(addIndexAction);
        AcknowledgedResponse indicesAliasesResponse =
                client.indices().updateAliases(request, RequestOptions.DEFAULT);
        log.info("indicesAliasesResponse response:{}", GSONUtil.obj2Json(indicesAliasesResponse));

    }

    // TODO
    public void getAlias(String name) {
        GetAliasesRequest request = new GetAliasesRequest();
        request.indices(name);
        try {
            GetAliasesResponse response = client.indices().getAlias(request, RequestOptions.DEFAULT);
            log.info("response :{}", GSONUtil.obj2Json(response));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }


    /**
     * 清除游标
     *
     * @param scrollIds scrollId
     */
    private void clearScrollRequest(Set<String> scrollIds) {
        if (ObjectUtil.isEmpty(scrollIds)) {
            return;
        }
        try {
            // 关闭scroll
            ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
            clearScrollRequest.setScrollIds(new ArrayList<>(scrollIds));
            client.clearScroll(clearScrollRequest, options);
            scrollIds.clear();
        } catch (Exception e) {
            log.warn(e.getMessage(), e);
        }
    }

    /**
     * 查询条件
     *
     * @param example   查询条件
     * @param needScore 是否需要分值
     * @return 查询对象
     */
    private SearchSourceBuilder createBuilder(QueryExample example, boolean needScore) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        if (ObjectUtil.isNotEmpty(example.getIncludeFields()) || ObjectUtil.isNotEmpty(example.getExcludeFields())) {
            searchSourceBuilder.fetchSource(example.getIncludeFields(), example.getExcludeFields());
        }
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        // 其他条件
        if (ObjectUtil.isNotEmpty(example.getItems())) {
            example.getItems().forEach(item -> {
                QueryOption opt = item.getOpt();
                if (opt == QueryOption.AGGREGATION) {
                    searchSourceBuilder.aggregation(item.getAggregate());
                } else {
                    QueryBuilder qb = item.toQueryBuilder();
                    switch (opt) {
                        case FILTER:
                            queryBuilder.filter(qb).boost(item.getBoost());
                            break;
                        case MUST:
                            queryBuilder.must(qb).boost(item.getBoost());
                            break;
                        case NOT:
                            queryBuilder.mustNot(qb).boost(item.getBoost());
                            break;
                        case SHOULD:
                            queryBuilder.should(qb).boost(item.getBoost());
                            queryBuilder.minimumShouldMatch(1);
                            break;
                        default:
                            throw new StatusException(StatusInfo.notSupported());
                    }
                }
            });
        }
        if (!needScore) {
            searchSourceBuilder.query(QueryBuilders.constantScoreQuery(queryBuilder));
        } else {
            searchSourceBuilder.query(queryBuilder);
        }
        return searchSourceBuilder;
    }

}
